﻿using System;

namespace CoreEx.Database.Query.Expressions
{
    /// <summary>
    /// Provides the fluent interface that targets a 'SELECT' query.
    /// </summary>
    public static class DbSelectQueryExtensions
    {
        
        private static readonly DbExpressionFactory _dbExpressionFactory = new DbExpressionFactory();


        /// <summary>
        /// Creates a <see cref="DbOrderByExpression"/> that represents ordering the result set.
        /// </summary>        
        /// <param name="dbSelectQuery">The target <see cref="DbQuery{TQueryExpression}"/>.</param>
        /// <param name="expressionSelector">A function used to specify an element in the 'ORDER BY' clause.</param>
        /// <param name="orderByExpressionType">Specifies the sort order direction.</param>
        /// <returns></returns>
        public static DbQuery<DbSelectExpression> OrderBy(this DbQuery<DbSelectExpression> dbSelectQuery, Func<DbExpressionFactory, DbExpression> expressionSelector, DbOrderByExpressionType orderByExpressionType)
        {
            var dbExpression = (DbExpression)_dbExpressionFactory.MakeOrderBy(orderByExpressionType, expressionSelector(_dbExpressionFactory));
            if (!dbSelectQuery.QueryExpression.OrderByExpression.IsNull())
                dbExpression = _dbExpressionFactory.List(new[] { dbSelectQuery.QueryExpression.FromExpression, dbExpression });
            dbSelectQuery.QueryExpression.OrderByExpression = dbExpression;
            return dbSelectQuery;
        }

        /// <summary>
        /// Creates a new <see cref="DbOrderByExpression"/> that represents an ascending ordering of the result set.        
        /// </summary>
        /// <param name="dbSelectQuery">The target <see cref="DbQuery{TQueryExpression}"/>.</param>
        /// <param name="expressionSelector">A function used to specify an element in the 'ORDER BY' clause.</param>
        public static DbQuery<DbSelectExpression> OrderByAscending(this DbQuery<DbSelectExpression> dbSelectQuery, Func<DbExpressionFactory, DbExpression> expressionSelector)
        {
            return OrderBy(dbSelectQuery,expressionSelector, DbOrderByExpressionType.Ascending);
        }

        /// <summary>
        /// Creates a new <see cref="DbOrderByExpression"/> that represents an descending ordering of the result set.
        /// </summary>
        /// <param name="dbSelectQuery">The target <see cref="DbQuery{TQueryExpression}"/>.</param>
        /// <param name="expressionSelector">A function used to specify an element in the 'ORDER BY' clause.</param>
        public static DbQuery<DbSelectExpression> OrderByDescending(this DbQuery<DbSelectExpression> dbSelectQuery, Func<DbExpressionFactory, DbExpression> expressionSelector)
        {
            return OrderBy(dbSelectQuery,expressionSelector, DbOrderByExpressionType.Ascending);
        }

        /// <summary>
        /// Limits the numbers of rows returned by the query.
        /// </summary>
        /// <param name="dbSelectQuery">The target <see cref="DbQuery{TQueryExpression}"/>.</param>
        /// <param name="expressionSelector">A function used to create a <see cref="DbExpression"/> that the number of rows to return.</param>
        /// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        public static DbQuery<DbSelectExpression> Take(this DbQuery<DbSelectExpression> dbSelectQuery, Func<DbExpressionFactory, DbExpression> expressionSelector)
        {
            dbSelectQuery.QueryExpression.TakeExpression = expressionSelector(_dbExpressionFactory);
            return dbSelectQuery;
        }

        /// <summary>
        /// Limits the numbers of rows returned by the query.
        /// </summary>
        /// <param name="dbSelectQuery">The target <see cref="DbQuery{TQueryExpression}"/>.</param>
        /// <param name="count">The number of rows to return.</param>
        /// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        public static DbQuery<DbSelectExpression> Take(this DbQuery<DbSelectExpression> dbSelectQuery, int count)
        {
            dbSelectQuery.QueryExpression.TakeExpression = _dbExpressionFactory.Constant(count);
            return dbSelectQuery;
            
        }

        /// <summary>
        /// Bypasses a specified number of rows in the result set.
        /// </summary>
        /// <param name="dbSelectQuery">The target <see cref="DbQuery{TQueryExpression}"/>.</param>
        /// <param name="expressionSelector">A function used to create a <see cref="DbExpression"/> that the number of rows to bypass.</param>
        /// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        public static DbQuery<DbSelectExpression> Skip(this DbQuery<DbSelectExpression> dbSelectQuery, Func<DbExpressionFactory, DbExpression> expressionSelector)
        {
            dbSelectQuery.QueryExpression.SkipExpression = expressionSelector(_dbExpressionFactory);
            return dbSelectQuery;
        }


        /// <summary>
        /// Bypasses a specified number of rows in the result set.
        /// </summary>
        /// <param name="dbSelectQuery">The target <see cref="DbQuery{TQueryExpression}"/>.</param>
        /// <param name="count">The number of rows to bypass in the result set</param>
        /// <returns><see cref="DbQuery{TQueryExpression}"/></returns>
        public static DbQuery<DbSelectExpression> Skip(this DbQuery<DbSelectExpression> dbSelectQuery, int count)
        {
            dbSelectQuery.QueryExpression.SkipExpression = _dbExpressionFactory.Constant(count);
            return dbSelectQuery;
        }
        
    }
}
